package com.hzjq.core.cmd

import android.os.Message
import com.hzjq.core.BlastDelegate
import com.hzjq.core.ErrorCode
import com.hzjq.core.bean.ErrorResult
import com.hzjq.core.callback.Callback
import com.hzjq.core.event.PioUnEnableEvent
import com.hzjq.core.loader.CmdExeLoader
import com.hzjq.core.receive.Receives
import com.hzjq.core.serial.SerialManager
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import java.util.concurrent.TimeUnit

class CmdExeImpl : CmdExeLoader {

    private val openPortCallback = OpenPortCallback()
    private var callback: Callback<Any>? = null
    private var retryCount = 0
    private val SEND_MSG_WHAT = 0x006785
    private var pollDisposable: Disposable? = null
    private var delayDisposable: Disposable? = null


    override fun <T> exeOnceCmd(cmd: ByteArray, callback: Callback<T>?) {
        cancel()
        this.callback = callback as Callback<Any>
        openPortCallback.isPoll = false
        openPortCallback.isFailedRetry = false
        openPortCallback.cmd = cmd
        BlastDelegate.getDelegate().getOnSendMessageLoader()
            .makeSurePortOpen(openPortCallback, callback)
    }


    override fun <T> exePollResultCmd(cmd: ByteArray, callback: Callback<T>?) {
        cancel()
        this.callback = callback as Callback<Any>
        openPortCallback.isPoll = true
        openPortCallback.isFailedRetry = false
        openPortCallback.cmd = cmd
        openPortCallback.maxDelay = -1
        openPortCallback.delay = -1
        retryCount = 0
        BlastDelegate.getDelegate().getOnSendMessageLoader()
            .makeSurePortOpen(openPortCallback, callback)
    }


    override fun <T> exePollResultCmd(cmd: ByteArray, maxDelay: Long, callback: Callback<T>?) {
        cancel()
        this.callback = callback as Callback<Any>
        openPortCallback.isPoll = true
        openPortCallback.isFailedRetry = false
        openPortCallback.cmd = cmd
        openPortCallback.maxDelay = maxDelay
        openPortCallback.delay = -1
        retryCount = 0
        BlastDelegate.getDelegate().getOnSendMessageLoader()
            .makeSurePortOpen(openPortCallback, callback)
    }

    override fun <T> exeDelayPollResultCmd(cmd: ByteArray, delay: Long, callback: Callback<T>?) {
        cancel()
        this.callback = callback as Callback<Any>
        openPortCallback.isPoll = true
        openPortCallback.isFailedRetry = false
        openPortCallback.cmd = cmd
        openPortCallback.maxDelay = -1
        openPortCallback.delay = delay
        retryCount = 0
        BlastDelegate.getDelegate().getOnSendMessageLoader()
            .makeSurePortOpen(openPortCallback, callback)
    }


    /**
     * 开始轮询结果
     */
    private fun startInterval(delay: Long,maxDelay: Long,cmd: ByteArray) {
        if(delay > 0){
            startPoll(BlastDelegate.getDelegate().getReceiveOutTime()+delay, cmd)
            delaySendData(delay, cmd)
        } else {
            startPoll(maxDelay, cmd)
            BlastDelegate.getDelegate().getOnSendMessageLoader().sendData(cmd)
        }
    }


    /**
     * 延时执行
     */
    private fun delaySendData(delay: Long,cmd: ByteArray){
        if(delayDisposable != null && !delayDisposable!!.isDisposed) {
            delayDisposable?.dispose()
        }
        delayDisposable = Observable.timer(delay,TimeUnit.MILLISECONDS,Schedulers.newThread())
            .subscribeOn(Schedulers.newThread())
            .observeOn(Schedulers.newThread())
            .subscribe {
                BlastDelegate.getDelegate().getOnSendMessageLoader().sendData(cmd)
            }
    }

    /**
     * 开始轮询
     */
    private fun startPoll(maxDelay: Long,cmd: ByteArray) {
        val message = Message.obtain()
        val delayCmd = DelayCmd()
        message.what = SEND_MSG_WHAT

        val delay = if(maxDelay > 0){
            maxDelay
        } else {
            BlastDelegate.getDelegate().getReceiveOutTime()
        }

        delayCmd.cmd = cmd
        delayCmd.maxDelay = delay

        message.obj = delayCmd

        sendMessageDelayed(message,delay)
    }


    /**
     * 延时发送超时检测数据
     */
    private fun sendMessageDelayed(msg:Message,delay:Long){
        if(pollDisposable != null && !pollDisposable!!.isDisposed) {
            pollDisposable?.dispose()
        }
        pollDisposable = Observable.timer(delay,TimeUnit.MILLISECONDS,Schedulers.newThread())
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                sendMessage(msg, delay)
            }
    }


    /**
     * 发送数据
     */
    private fun sendMessage(msg:Message,delay:Long){
        val cmd = msg.obj as DelayCmd
        if (retryCount > 0) {
            val b =
                retryCount > if (openPortCallback.isFailedRetry) BlastDelegate.getDelegate()
                    .getFailedRetryCount()
                else BlastDelegate.getDelegate().getOutTimeRetryCount()
            if (b) {
                Receives.getInstance().onCallbackCmdFailed(cmd.cmd)
                callback?.onError(ErrorCode.getErrorResult(if (openPortCallback.isFailedRetry) -25 else -26))
                EventBus.getDefault().post(PioUnEnableEvent())
                SerialManager.getInstance().enablePio(true)
                cancel()
                return
            }
            BlastDelegate.getDelegate().getOnSendMessageLoader().sendData(cmd.cmd)
            callback?.onRetryCountChanged(
                retryCount,
                if (openPortCallback.isFailedRetry) "失败重试" else "超时重试"
            )
        }
        retryCount++
        sendMessageDelayed(msg,delay)
    }

    /**
     * 如果ack长度小于最小长度时重试
     */
    fun retryCmdByAckLessThenMinLength(cmd: ByteArray) {
        cancel()
        openPortCallback.isPoll = true
        openPortCallback.isFailedRetry = true
        openPortCallback.cmd = cmd
        BlastDelegate.getDelegate().getOnSendMessageLoader()
            .makeSurePortOpen(openPortCallback, callback)
    }


    private inner class OpenPortCallback : Callback<Boolean> {
        var isPoll = false
        var isFailedRetry = false
        var maxDelay = -1L
        var delay = -1L
        lateinit var cmd: ByteArray
        override fun onResult(t: Boolean) {
            if (t) {
                if (!isPoll) {
                    BlastDelegate.getDelegate().getOnSendMessageLoader().sendData(cmd)
                } else {
                    startInterval(delay,maxDelay,cmd)
                }
            }
        }

        override fun onError(errorCode: ErrorResult) {

        }

        override fun onRetryCountChanged(retryCount: Int, action: String) {

        }
    }

    override fun cancel() {
        retryCount = 0
        if(pollDisposable != null && !pollDisposable!!.isDisposed) {
            pollDisposable?.dispose()
        }
        if(delayDisposable != null && !delayDisposable!!.isDisposed) {
            delayDisposable?.dispose()
        }
    }

    class DelayCmd{
        var maxDelay = -1L
        lateinit var cmd: ByteArray
    }
}